package cn.yx.common.mybatis.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.yx.common.core.enums.OrderByTypeEnum;
import cn.yx.common.core.pojo.singletable.SingleCond;
import cn.yx.common.core.util.StringUtil;
import cn.yx.common.core.util.reflect.ReflectUtil;
import cn.yx.common.mybatis.pojo.SingleDo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * <p>MyBatis-plus服务类，用于获取各类Wrapper对象</p>
 *
 * @param <Do>   DO类
 * @param <Cond> Cond类
 * @author Wgssmart
 */
@Component
public class MybatisPlusWrapperService<Do extends SingleDo, Cond extends SingleCond> {

    private static final String SERIAL_VERSION_UID = "serialVersionUID";
    private static final String PROPERTY_ID = "id";
    private static final String PROPERTY_VERSION = "version";
    private static final String PROPERTY_IS_ACTIVE = "isActive";
    private static final String PROPERTY_CREATE_BY = "createBy";
    private static final String PROPERTY_CREATE_TIME = "createTime";
    private static final String PROPERTY_UPDATE_BY = "updateBy";
    private static final String PROPERTY_UPDATE_TIME = "updateTime";

    private static final String PROPERTY_SORT_FIELDS = "sortFields";
    private static final String PROPERTY_LIKE_FIELDS = "likeFields";
    private static final String PROPERTY_MAX = "Max";
    private static final String PROPERTY_MIN = "Min";
    private static final String PROPERTY_IN = "In";

    private static final String COLUMN_CREATE_TIME = "create_time";
    private static final String COLUMN_UPDATE_TIME = "update_time";
    private static final String COLUMN_SUFFIX_MIN = "_min";
    private static final String COLUMN_SUFFIX_MAX = "_max";
    private static final String COLUMN_SUFFIX_IN = "_in";

    /**
     * 在更新所有字段时不需要更新的字段
     */
    private static final List<String> NO_UPDATE_COLUMN_LIST = List.of(SERIAL_VERSION_UID, PROPERTY_ID,
            PROPERTY_VERSION, PROPERTY_IS_ACTIVE, PROPERTY_CREATE_BY, PROPERTY_CREATE_TIME);

    /**
     * 查询条件中不需要设置到where部分的字段
     */
    private static final List<String> NO_QUERY_WHERE_COLUMN_LIST = List.of(SERIAL_VERSION_UID,
            PROPERTY_LIKE_FIELDS, PROPERTY_SORT_FIELDS);

    /**
     * 从DO对象构建UpdateWrapper对象，DO对象包含了需要更新的目标数据，更新条件为数据表中的主键id=DO对象中的主键id值
     *
     * @param entity          DO对象，需要更新的目标对象，包含更新条件
     * @param updateAllColumn 是否更新所有字段，如果是，则可能会把非空字段更新为null
     * @return UpdateWrapper对象
     */
    public UpdateWrapper<Do> buildUpdateWrapper(Do entity, boolean updateAllColumn) {
        UpdateWrapper<Do> updateWrapper = new UpdateWrapper<>();
        buildUpdateWrapperSet(updateWrapper, entity, updateAllColumn);
        buildUpdateWrapperWhereId(updateWrapper, entity);
        return updateWrapper;
    }

    /**
     * 从DO和Cond对象中构建UpdateWrapper对象，DO对象包含了需要更新的目标数据，Cond对象包含了更新的条件
     *
     * @param entity          DO对象
     * @param updateCond      条件对象
     * @param updateAllColumn 是否更新所有字段，如果是，则可能会把非空字段更新为null
     * @return UpdateWrapper对象
     */
    public UpdateWrapper<Do> buildUpdateWrapper(Do entity, Cond updateCond, boolean updateAllColumn) {
        UpdateWrapper<Do> updateWrapper = new UpdateWrapper<>();
        buildUpdateWrapperSet(updateWrapper, entity, updateAllColumn);
        buildUpdateWrapperWhere(updateWrapper, updateCond);
        buildUpdateWrapperWhereLike(updateWrapper, entity, updateCond);
        return updateWrapper;
    }

    /**
     * 从DO对象中获取UpdateWrapper update set部分
     *
     * @param updateWrapper
     * @param entity          DO对象
     * @param updateAllColumn 是否更新所有字段，如果是，则可能会把非空字段更新为null
     */
    private void buildUpdateWrapperSet(UpdateWrapper<Do> updateWrapper, Do entity, boolean updateAllColumn) {
        Field[] entityFields = entity.getClass().getDeclaredFields();
        for (Field field : entityFields) {
            String fieldName = field.getName();
            if (NO_UPDATE_COLUMN_LIST.contains(fieldName)) {
                // 指定的字段不需要更新
                continue;
            }
            Object filedValue = ReflectUtil.invokeGetter(entity, fieldName);
            if (updateAllColumn) {
                updateWrapper.set(StringUtil.camelToUnderline(fieldName), filedValue);
            } else if (Objects.nonNull(filedValue)) {
                updateWrapper.set(StringUtil.camelToUnderline(fieldName), filedValue);
            }
        }
    }

    /**
     * 从DO对象中获取主键id，并设置到where条件部分：id = 指定的id
     *
     * @param updateWrapper
     * @param entity        DO对象
     */
    private void buildUpdateWrapperWhereId(UpdateWrapper<Do> updateWrapper, Do entity) {
        updateWrapper.eq(PROPERTY_ID, ReflectUtil.invokeGetter(entity, PROPERTY_ID));
    }

    /**
     * 从Cond对象中获取UpdateWrapper update where条件部分
     *
     * @param updateWrapper
     * @param updateCond    Cond对象
     */
    private void buildUpdateWrapperWhere(UpdateWrapper<Do> updateWrapper, Cond updateCond) {
        Field[] queryFields = updateCond.getClass().getDeclaredFields();
        for (Field field : queryFields) {
            String fieldName = field.getName();
            if (SERIAL_VERSION_UID.equals(fieldName)
                    || PROPERTY_LIKE_FIELDS.equals(fieldName)) {
                continue;
            }
            Object fieldValue = ReflectUtil.invokeGetter(updateCond, fieldName);
            if (Objects.nonNull(fieldValue)) {
                String column = StringUtil.camelToUnderline(fieldName);
                if (fieldName.endsWith(PROPERTY_MAX)) {
                    updateWrapper.le(column.replace(COLUMN_SUFFIX_MAX, StringUtil.EMPTY_STRING), fieldValue);
                } else if (fieldName.endsWith(PROPERTY_MIN)) {
                    updateWrapper.ge(column.replace(COLUMN_SUFFIX_MIN, StringUtil.EMPTY_STRING), fieldValue);
                } else if (fieldName.endsWith(PROPERTY_IN)) {
                    updateWrapper.in(column.replace(COLUMN_SUFFIX_IN, StringUtil.EMPTY_STRING), fieldValue);
                } else {
                    updateWrapper.eq(column, fieldValue);
                }
            }
        }
    }

    /**
     * 从Cond对象中获取UpdateWrapper update where like条件部分
     *
     * @param updateWrapper
     * @param entity        DO对象
     * @param updateCond    Cond对象
     */
    @SuppressWarnings({"unchecked"})
    private void buildUpdateWrapperWhereLike(UpdateWrapper<Do> updateWrapper, Do entity, Cond updateCond) {
        List<String> likeFieldNames = ReflectUtil.invokeGetter(updateCond, PROPERTY_LIKE_FIELDS, List.class);
        if (CollectionUtil.isNotEmpty(likeFieldNames)) {
            for (String fieldName : likeFieldNames) {
                Object fieldValue = ReflectUtil.invokeGetter(entity, fieldName);
                if (Objects.nonNull(fieldValue)) {
                    updateWrapper.like(StringUtil.camelToUnderline(fieldName), fieldValue);
                }
            }
        }
    }

    /**
     * 从Cond对象构建CondWrapper对象的where条件部部分，包含排序
     *
     * @param selectCond Cond条件查询对象
     * @return CondWrapper对象
     */
    public QueryWrapper<Do> buildCondWrapper(Cond selectCond) {
        QueryWrapper<Do> queryWrapper = new QueryWrapper<>();
        buildCondWrapperWhere(queryWrapper, selectCond);
        buildCondWrapperWhereLike(queryWrapper, selectCond);
        buildCondWrapperSortColumns(queryWrapper, selectCond);
        return queryWrapper;
    }

    /**
     * 获取条件查询的where条件部分
     *
     * @param queryWrapper
     * @param selectCond   Cond查询对象
     */
    public void buildCondWrapperWhere(QueryWrapper<Do> queryWrapper, Cond selectCond) {
        Field[] queryFields = selectCond.getClass().getDeclaredFields();
        for (Field field : queryFields) {
            String fieldName = field.getName();
            if (NO_QUERY_WHERE_COLUMN_LIST.contains(fieldName)) {
                continue;
            }
            Object fieldValue = ReflectUtil.invokeGetter(selectCond, fieldName);
            if (Objects.nonNull(fieldValue)) {
                String column = StringUtil.camelToUnderline(fieldName);
                if (fieldName.endsWith(PROPERTY_MAX)) {
                    queryWrapper.le(column.replace(COLUMN_SUFFIX_MAX, StringUtil.EMPTY_STRING), fieldValue);
                } else if (fieldName.endsWith(PROPERTY_MIN)) {
                    queryWrapper.ge(column.replace(COLUMN_SUFFIX_MIN, StringUtil.EMPTY_STRING), fieldValue);
                } else {
                    queryWrapper.eq(column, fieldValue);
                }
            }
        }
    }

    /**
     * 获取条件查询的where条件的like部分
     *
     * @param queryWrapper
     * @param selectCond   Cond查询对象
     */
    @SuppressWarnings({"unchecked"})
    public void buildCondWrapperWhereLike(QueryWrapper<Do> queryWrapper, Cond selectCond) {
        List<String> likeFieldNames = ReflectUtil.invokeGetter(selectCond, PROPERTY_LIKE_FIELDS, List.class);
        if (CollectionUtil.isNotEmpty(likeFieldNames)) {
            for (String fieldName : likeFieldNames) {
                Object fieldValue = ReflectUtil.invokeGetter(selectCond, fieldName);
                if (Objects.nonNull(fieldValue)) {
                    queryWrapper.like(StringUtil.camelToUnderline(fieldName), fieldValue);
                }
            }
        }
    }

    /**
     * 获取条件查询的where条件的order by部分，默认以更新时间和创建时间为最后的排序字段
     *
     * @param queryWrapper
     * @param selectCond   Cond查询对象
     */
    @SuppressWarnings({"unchecked"})
    public void buildCondWrapperSortColumns(QueryWrapper<Do> queryWrapper, Cond selectCond) {
        Map<String, String> sortFields = ReflectUtil.invokeGetter(selectCond, PROPERTY_SORT_FIELDS, Map.class);
        if (CollectionUtil.isNotEmpty(sortFields)) {
            sortFields.forEach((field, orderType) -> {
                String column = StringUtil.camelToUnderline(field);
                if (OrderByTypeEnum.ASC.getValue().equalsIgnoreCase(orderType)) {
                    queryWrapper.orderByAsc(column);
                } else if (OrderByTypeEnum.DESC.getValue().equalsIgnoreCase(orderType)) {
                    queryWrapper.orderByDesc(column);
                } else {
                    queryWrapper.orderByAsc(column);
                }
            });
        }
        queryWrapper.orderByDesc(COLUMN_UPDATE_TIME);
        queryWrapper.orderByDesc(COLUMN_CREATE_TIME);
    }

}
